<template>
  <div class="webgl-snowflake">
    <h1>WEBGL雪花图形</h1>
    <p>使用WebGL绘制雪花图形，展示如何使用基本图形元素组合成对称的雪花图案。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>雪花图形展示</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="demoCanvas" width="400" height="300"></canvas>
        <div class="control-panel">
          <el-form :model="snowflakeForm" label-width="100px">
            <el-form-item label="线条颜色">
              <el-color-picker v-model="snowflakeForm.lineColor" @change="updateSnowflake" />
            </el-form-item>

            <el-form-item label="雪花大小">
              <el-slider
                v-model="snowflakeForm.size"
                :min="0.1"
                :max="0.8"
                :step="0.1"
                @change="updateSnowflake"
              />
            </el-form-item>
            <el-form-item label="旋转角度">
              <el-slider
                v-model="snowflakeForm.rotation"
                :min="0"
                :max="360"
                @change="updateSnowflake"
              />
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
attribute vec4 a_Position;
uniform float u_Size;
uniform float u_Rotation;
void main() {
  float radian = u_Rotation * 3.14159 / 180.0;
  float cosB = cos(radian);
  float sinB = sin(radian);
  mat4 rotation = mat4(
    cosB, -sinB, 0.0, 0.0,
    sinB, cosB, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0
  );
  vec4 position = vec4(a_Position.xy * u_Size, 0.0, 1.0);
  gl_Position = rotation * position;
}

// 片元着色器
precision mediump float;
uniform vec4 u_Color;
void main() {
  gl_FragColor = u_Color;
}

// JavaScript代码
// 定义初始三角形顶点
const initialVertices = [
  new vec2(-0.866, -0.5),  // 左下角
  new vec2(0, 1),          // 顶部
  new vec2(0.866, -0.5)    // 右下角
];

// 细分线段生成Koch曲线
function divideLineSegment(a, b, depth = 0) {
  const c = vec2.lerp(a, b, 1/3);
  const d = vec2.lerp(a, b, 2/3);
  const e = vec2.rotate(d, c, Math.PI/3);

  if (depth 小于 MAX_DEPTH) {
    depth++;
    divideLineSegment(a, c, depth);
    divideLineSegment(c, e, depth);
    divideLineSegment(e, d, depth);
    divideLineSegment(d, b, depth);
  } else {
    points.push(a.x, a.y, c.x, c.y, e.x, e.y, d.x, d.y, b.x, b.y);
  }
}

// 细分三角形的三条边
function divideTriangle(a, b, c) {
  points.length = 0;
  divideLineSegment(a, b);
  divideLineSegment(b, c);
  divideLineSegment(c, a);
  return points;
}

// 生成Koch雪花的顶点数据
const snowflakeVertices = divideTriangle(
  initialVertices[0],
  initialVertices[1],
  initialVertices[2]
);

// 绘制雪花图形
gl.uniform4fv(u_Color, [r, g, b, 1.0]);
gl.drawArrays(gl.LINES, 0, snowflakeVertices.length / 2);
        </code></pre>
      </div>
    </el-card>

    <el-card class="info-section">
      <template #header>
        <div class="card-header">
          <h3>实现说明</h3>
        </div>
      </template>
      <el-descriptions :column="1" border>
        <el-descriptions-item label="绘制方式">
          使用LINES模式绘制雪花的主要分支线段
        </el-descriptions-item>
        <el-descriptions-item label="顶点数据">
          通过中心点和六个方向的端点定义雪花的基本形状
        </el-descriptions-item>
        <el-descriptions-item label="变换控制">
          支持旋转变换和缩放变换，实现雪花的旋转和大小调整
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { WebGLUtils, colorUtils, vec2 } from '@/utils/webgl'

const demoCanvas = ref<HTMLCanvasElement | null>(null)
const gl = ref<WebGLRenderingContext | null>(null)
const shaderProgram = ref<WebGLProgram | null>(null)
const webglUtils = ref<WebGLUtils | null>(null)

const snowflakeForm = reactive({
  lineColor: '#409EFF',

  size: 0.5,
  rotation: 0
})

// 定义初始三角形顶点（正三角形，以原点为中心）
const initialVertices = [
  new vec2(-0.866, -0.5),
  new vec2(0, 1),
  new vec2(0.866, -0.5)
]

const points: number[] = []
const MAX_DEPTH = 4

// 细分线段生成Koch曲线
function divideLineSegment(a: vec2, b: vec2, depth = 0) {
  const c = vec2.lerp(a, b, 1 / 3)
  const d = vec2.lerp(a, b, 2 / 3)
  const e = vec2.rotate(d, c, Math.PI / 3)

  if (depth < MAX_DEPTH) {
    depth++
    divideLineSegment(a, c, depth)
    divideLineSegment(c, e, depth)
    divideLineSegment(e, d, depth)
    divideLineSegment(d, b, depth)
  } else {
    points.push(a.x, a.y, c.x, c.y, e.x, e.y, d.x, d.y, b.x, b.y)
  }
}

// 细分三角形的三条边
function divideTriangle(a: vec2, b: vec2, c: vec2) {
  points.length = 0
  divideLineSegment(a, b)
  divideLineSegment(b, c)
  divideLineSegment(c, a)
  return points
}

// 生成Koch雪花的顶点数据
const snowflakeVertices = divideTriangle(
  initialVertices[0],
  initialVertices[1],
  initialVertices[2]
)

// 初始化WebGL
const initWebGL = () => {
  if (!demoCanvas.value) return

  // 获取WebGL上下文
  gl.value = demoCanvas.value.getContext('webgl')
  if (!gl.value) {
    console.error('Failed to get WebGL context')
    return
  }

  // 创建WebGL工具实例
  webglUtils.value = new WebGLUtils(gl.value)

  // 顶点着色器源码
  const vertexShaderSource = `
    attribute vec4 a_Position;
    uniform float u_Size;
    uniform float u_Rotation;
    void main() {
      float radian = u_Rotation * 3.14159 / 180.0;
      float cosB = cos(radian);
      float sinB = sin(radian);
      mat4 rotation = mat4(
        cosB, -sinB, 0.0, 0.0,
        sinB, cosB, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0
      );
      vec4 position = vec4(a_Position.xy * u_Size, 0.0, 1.0);
      gl_Position = rotation * position;
    }
  `

  // 片元着色器源码
  const fragmentShaderSource = `
    precision mediump float;
    uniform vec4 u_Color;
    void main() {
      gl_FragColor = u_Color;
    }
  `

  // 创建着色器
  const vertexShader = webglUtils.value.createShader(gl.value.VERTEX_SHADER, vertexShaderSource)
  const fragmentShader = webglUtils.value.createShader(gl.value.FRAGMENT_SHADER, fragmentShaderSource)
  if (!vertexShader || !fragmentShader) return

  // 创建程序并链接着色器
  shaderProgram.value = webglUtils.value.createProgram(vertexShader, fragmentShader)
  if (!shaderProgram.value) return

  gl.value.useProgram(shaderProgram.value)

  // 更新雪花
  updateSnowflake()
}

// 更新雪花属性并重绘
const updateSnowflake = () => {
  if (!gl.value || !shaderProgram.value || !webglUtils.value) return

  // 设置清除颜色并清除
  gl.value.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.value.clear(gl.value.COLOR_BUFFER_BIT)

  // 转换颜色
  const color = colorUtils.hexToRgb(snowflakeForm.lineColor)

  // 获取uniform位置
  const u_Color = gl.value.getUniformLocation(shaderProgram.value, 'u_Color')
  const u_Size = gl.value.getUniformLocation(shaderProgram.value, 'u_Size')
  const u_Rotation = gl.value.getUniformLocation(shaderProgram.value, 'u_Rotation')

  // 设置uniform值
  gl.value.uniform4f(u_Color, color.r, color.g, color.b, 1.0)
  gl.value.uniform1f(u_Size, snowflakeForm.size)
  gl.value.uniform1f(u_Rotation, snowflakeForm.rotation)

  // 创建并绑定缓冲区
  const vertexBuffer = webglUtils.value.createBuffer(new Float32Array(snowflakeVertices))
  if (!vertexBuffer) return

  // 设置顶点位置属性
  const a_Position = gl.value.getAttribLocation(shaderProgram.value, 'a_Position')
  gl.value.vertexAttribPointer(a_Position, 2, gl.value.FLOAT, false, 0, 0)
  gl.value.enableVertexAttribArray(a_Position)

  // 绘制雪花
  gl.value.drawArrays(gl.value.LINES, 0, snowflakeVertices.length / 2)
}

// 组件挂载时初始化WebGL
onMounted(() => {
  initWebGL()
})
</script>

<style lang="scss" scoped>
.webgl-snowflake {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    color: #303133;
    margin-bottom: 20px;
  }

  p {
    margin-bottom: 30px;
  }

  .demo-section,
  .code-section,
  .info-section {
    margin: 30px 0;

    .card-header {
      display: flex;
      align-items: center;

      h3 {
        margin: 0;
        color: #409EFF;
      }
    }
  }

  .demo-section {
    .demo-content {
      display: flex;
      gap: 20px;

      canvas {
        border: 1px solid #dcdfe6;
        background-color: #000;
      }

      .control-panel {
        flex: 1;
        padding: 20px;
        background-color: #f8f9fa;
        border-radius: 4px;
      }
    }
  }

  .code-section,
  .info-section {
    .code-content {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;

      code {
        font-family: monospace;
        color: #409EFF;
      }

      pre {
        margin: 0;
        white-space: pre-wrap;
      }
    }
  }
}
</style>